#include <QDebug>
#include <QThread>
#include <QWriteLocker>
#include <QReadWriteLock>
#include <QSharedPointer>
#include <QtCore/QJsonValue>
#include <QtCore/QJsonArray>
#include <QtCore/QJsonObject>
#include <QtCore/QJsonDocument>
#include <QtCore/QJsonParseError>
#include <QtCore/QLoggingCategory>

#include "openharmonycamera.h"
#include "QtCore/QOpenHarmonyJsObject"
#include "QtCore/QOpenHarmonyJsEnvironment"
#include "QtCore/QOpenHarmonyJsObjectLoader"

QT_BEGIN_NAMESPACE

Q_LOGGING_CATEGORY(qtCameraInfo, "qt.multimedia.plugins.openharmony.camerainfo")

using CameraMap = QHash<QString, OPenHarmonyCamera *>;

Q_GLOBAL_STATIC(CameraMap, cameras)
Q_GLOBAL_STATIC(QReadWriteLock, rwLock)

class OPenHarmonyCameraPrivate : public QObject
{
    Q_OBJECT

public:
    OPenHarmonyCameraPrivate();
    ~OPenHarmonyCameraPrivate();

    Q_INVOKABLE bool init(const QString &cameraId);

    static QStringList getIdOfCameras();
    static void getCameraInfo(const QString &id, OPenHarmonyCameraInfo *info);
public:
    QString m_cameraId;
    static QSharedPointer<QOpenHarmonyJsObject> m_jsCamera;
};
QSharedPointer<QOpenHarmonyJsObject> OPenHarmonyCameraPrivate::m_jsCamera(Q_NULLPTR);

OPenHarmonyCameraPrivate::OPenHarmonyCameraPrivate()
    : QObject()
{
    m_jsCamera = qJsObjectLoader->create("JsCameraManager");
}

OPenHarmonyCameraPrivate::~OPenHarmonyCameraPrivate()
{

}

bool OPenHarmonyCameraPrivate::init(const QString &cameraId)
{
    Q_UNUSED(cameraId);
    return false;
}

QStringList OPenHarmonyCameraPrivate::getIdOfCameras()
{
    if (m_jsCamera.isNull()){
        m_jsCamera = qJsObjectLoader->create("JsCameraManager");
    }
    QStringList ids = m_jsCamera->call<QStringList>("idOfCameras");
    return ids;
}

void OPenHarmonyCameraPrivate::getCameraInfo(const QString &id, OPenHarmonyCameraInfo *info)
{
    Q_ASSERT(info);
    if (m_jsCamera.isNull()){
        m_jsCamera = qJsObjectLoader->create("JsCameraManager");
    }

    QString json = m_jsCamera->call<QString>("cameraInfo", id);
    if (json.isEmpty())
        return;

    QJsonParseError error;
    QJsonDocument doc = QJsonDocument::fromJson(json.toLocal8Bit(), &error);
    if (QJsonParseError::NoError != error.error) {
        qCWarning(qtCameraInfo) << "parase camera info from openharmony: " << error.errorString();
        return;
    }

    QJsonObject obj = doc.object();
    QString cameraId = obj.value(QStringLiteral("cameraId")).toString();
    info->name = cameraId.toLocal8Bit();
    int pos = obj.value(QStringLiteral("cameraPosition")).toInt();
    info->position = QCamera::Position(pos);

    static QHash<int, QString> sCameraTypeDes{
        { 3, QStringLiteral("Telephoto camera") },
        { 1, QStringLiteral("Wide Angle Len Camera") },
        { 2, QStringLiteral("Ultra wide Angle camera") },
        { 0, QStringLiteral("The camera type was not specified") },
        { 4, QStringLiteral("Camera with depth of field information") }
    };

            static QHash<int, QString> sConnectionTypeDes{
                                                            { 0, QStringLiteral(" Built-in Camera") },
                                                            { 1, QStringLiteral(" Usb-connected camera") },
                                                            { 2, QStringLiteral(" Remotely connected camera") },
                                                            };

    int cameraType = obj.value("cameraType").toInt();
    int connectType = obj.value("connectionType").toInt();
    info->description = sCameraTypeDes.value(cameraType) + sConnectionTypeDes.value(connectType);
    info->orientation = 0; //TODO Returns the physical orientation of the camera sensor.
}

OPenHarmonyCamera::~OPenHarmonyCamera()
{

}

QString OPenHarmonyCamera::cameraId() const
{
    Q_D(const OPenHarmonyCamera);
    return d->m_cameraId;
}

QStringList OPenHarmonyCamera::getIdOfCameras()
{
    return std::move(OPenHarmonyCameraPrivate::getIdOfCameras());
}

OPenHarmonyCamera *OPenHarmonyCamera::open(const QString &cameraId)
{
    OPenHarmonyCameraPrivate *d = new OPenHarmonyCameraPrivate();
    QThread *worker = new QThread;
    worker->start();
    d->moveToThread(worker);
    connect(worker, &QThread::finished, d, &OPenHarmonyCameraPrivate::deleteLater);
    bool ok = true;
    QMetaObject::invokeMethod(d, "init", Qt::BlockingQueuedConnection, Q_RETURN_ARG(bool, ok), Q_ARG(const QString&, cameraId));
    if (!ok) {
        worker->quit();
        worker->wait(5000);
        delete worker;
        return 0;
    }

    OPenHarmonyCamera *q = new OPenHarmonyCamera(d, worker);
    QWriteLocker locker(rwLock);
    cameras->insert(cameraId, q);

    return q;
}

void OPenHarmonyCamera::getCameraInfo(const QString &id, OPenHarmonyCameraInfo *info)
{
    OPenHarmonyCameraPrivate::getCameraInfo(id, info);
}

OPenHarmonyCamera::OPenHarmonyCamera(OPenHarmonyCameraPrivate *d, QThread *worker) : QObject()
      , d_ptr(d)
      , m_worker(worker)
{

}

QT_END_NAMESPACE

#include "openharmonycamera.moc"
